/* 
 * This program (together with send_middlebox and recv_public_trigger)
 * illustrates how a sender can use a stack of IDs to send the
 * packets through a sequence of middleboxes. 
 * Usage: 
 *   > recv_public_trigger <cfg_file.xml>
 *   > middlebox <cfg_file.xml>
 *   > ...
 *   > middlebox <cfg_file.xml>
 *   > send_middlebox <cfg_file.xml> <id1> <id2> ... <idn>
 * (id1, id2, ..., id(n-1) are the IDs of the triggers inserted by 
 *  middleboxes, and idn represents the ID of the trigger inserted
 *  by the receiver. These IDs are printed by each middlebox instance 
 *  and by recv_public_middlebox, respectively)
 *
 *  The only operation performed by the middlebox is to add 1000 to the
 *  sequence number of each packet. 
 * 
 *  Note: Except for the process_packet function, the code in this file 
 *        is identical to receive_public_trigger. 
 *
 */

#include <stdio.h>

#include "i3.h"
#include "i3_client_api.h"
#include "utils.h"

/* callbacks */
void process_packet(cl_trigger *t, void *data, void *fun_ctx)
{
  long seq;

  cbk_packet *tmp_cbk_pkt = (cbk_packet *) data;
  cl_buf *clb = tmp_cbk_pkt->clb;
  int stack_len = tmp_cbk_pkt->stack_len;
  
  if (clb->data_len) {
    /* read sequence number and add 10000 to it */ 
    seq = ntohl(*((long *)(clb->data)));
    printf("recv. seq. #: %ld\n", seq);
    seq += 10000;
    seq = htonl(seq);
    memcpy(clb->data, &seq, sizeof(long));
    clb->data_len = sizeof(long);

    /* send packet */
    if (stack_len >= 1) {
      cl_send_stack(tmp_cbk_pkt->ids, stack_len, clb, 0);
    } else
      printf("warning: stack_empty!\n");
  }

  //The callback function is responsible for free this memory.
  free(tmp_cbk_pkt);
}


void constraint_failed(cl_trigger *t, void *data, void* fun_ctx)
{
  printf("Trigger constraint failed\n");
}

void trigger_inserted(cl_trigger *t, void *data, void* fun_ctx)
{
  printf("Trigger inserted\n");
}

void trigger_failure(cl_trigger *t, void *data, void* fun_ctx)
{
  printf("Trigger failed\n");

  /* reinsert trigger */
  cl_insert_trigger(t, 0);
}

int i3_main_loop(char *cfg_file)
{
  ID  id;
  Key key;
  cl_trigger *t;
  
  /* initialize context */
  cl_init(cfg_file);

  /* 
   * Create and insert triggers (id, R) respectively.
   * All triggers are r-constrained 
   */
  /* chose a random ID and key */
  set_random_id(&id);
  set_random_key(&key);

  /* make the trigger left constrained; left constrained
   * triggers are resistant to eavesdropping and impersonation attacks,
   * since an attacker cannot insert a trigger with the same ID unless
   * it knows the key (rkey)
   */
  t = cl_create_trigger(&id, ID_LEN_BITS, &key, 
			CL_TRIGGER_CFLAG_L_CONSTRAINT|CL_TRIGGER_CFLAG_PUBLIC);
  /* print trigger's ID */
  printf("trigger's ID: "); print_id(&id);

  /* insert trigger */
  cl_insert_trigger(t, 0);

  /* associate callbacks with the inserted trigger */
  cl_register_trigger_callback(t, CL_CBK_TRIGGER_CONSTRAINT_FAILED, 
			       constraint_failed, NULL);
  cl_register_trigger_callback(t, CL_CBK_RECEIVE_PACKET,
			       process_packet, NULL);
  cl_register_trigger_callback(t, CL_CBK_TRIGGER_INSERTED, 
			       trigger_inserted, NULL);
  cl_register_trigger_callback(t, CL_CBK_TRIGGER_REFRESH_FAILED, 
			       trigger_failure, NULL);

  /* loop and call cl_select */
  cl_loop();

  /* remove & destroy trigger */
  cl_destroy_trigger(t);
  
  /* destroy context */
  cl_exit();
}


int main(int argc, char **argv)
{
  if (argc != 2) {
    printf("%s <cfg_file.xml>\n", argv[0]);
    exit(-1);
  }
  i3_main_loop(argv[1]);
  return -1;
}

